AddCSLuaFile("cl_init.lua");
AddCSLuaFile("cl_hud.lua");
AddCSLuaFile("cl_vgui.lua");
AddCSLuaFile("cl_scoreboard.lua");
AddCSLuaFile("shared.lua");

include("sv_download.lua");

include("shared.lua");
include("sv_player.lua");

local whitelist = {
	"models/props_gameplay/resupply_locker.mdl",
	"models/props_gameplay/cap_point_base.mdl",
	"models/props_vehicles/train_enginecar.mdl",
	"models/props_skybox/train_enginecar_skybox.mdl",
	"models/props_mining/computer_industrial03.mdl",
	"models/props_spytech/corkboard01.mdl",
	"models/props_spytech/work_table001.mdl",
	"models/props_mining/computer_industrial04.mdl",
	"models/props_mining/computer_industrial01.mdl",
	"models/props_mining/computer_industrial02.mdl",
	"models/props_spytech/wall_clock.mdl",
	"models/props_spytech/exit_sign01.mdl",
	"models/props_spytech/intercom.mdl",
	"models/props_lights/light_cone_farm.mdl",
	"models/props_2fort/lantern001.mdl",
	"models/props_nucleus/mp_capbottom.mdl",
	"models/props_nucleus/mp_captop.mdl",
	"models/props_gameplay/sign_gameplay01.mdl"
};

local function RemoveDoors()
	local map = game.GetMap();
	if (map:Left(6) == "arena_") then
		for k, v in pairs(ents.FindByClass("func_brush")) do
			v:Remove();
		end
		/*for k, v in pairs(ents.FindByClass("func_door")) do
			if (!table.HasValue(whitelist, v:GetModel())) then
				//v:Remove();
			end
		end
		for k, v in pairs(ents.FindByClass("prop_dynamic")) do
			if (!table.HasValue(whitelist, v:GetModel())) then
				//v:Remove();
			end
		end*/
		for k, v in pairs(ents.FindByClass("info_player_start")) do
			v:Remove();
		end
		for k, v in pairs(ents.FindByClass("logic_relay")) do
			v:Fire("trigger");
		end
		for k, v in pairs(ents.FindByClass("func_door")) do
			v:Fire("open");
		end
	end
end

function GM:Initialize()
	timer.Simple(.1, RemoveDoors);
	self.RoundTime = 0;
	self.CanAdd = false;
end


function GM:PlayerLoadout(ply)
	if (ply:IsChimera()) then //rawr chimera blahhhh
		ply:Give("uchimera_weapon");
	end
end


function GM:PlayerSetModel(ply)  //Isn't this supposed to get called automatically?
	if (ply:IsChimera()) then
		ply:SetModel("models/nintendo/Mother/uchimeragib.mdl");
		//ply:SetModel(self.ChimeraMdl);
		ply:AddEffects(EF_NOSHADOW | EF_NODRAW);
	else
		ply:SetModel("models/nintendo/mother/pigmask.mdl");
		//ply:SetModel(self.PlayerMdl);
	end
end


function GM:PlayerInitialSpawn(ply)
	
	/*if (self.ShowSplash) then
		ply:SendLua("GAMEMODE:ShowSplashScreen()");
	end*/
	
	ply:SetSprint(1);
	ply.UCSprinting = false;
	ply.RestMsg = false;
	ply.SprintRegen = true;
	ply:SetNWBool("UCScared", false);
	ply.SpecN = 0;
	ply.Speccing = false;
	ply:SetNWEntity("UCSpec", ply);
	ply:SetNWInt("SpecMode", 1);
	ply:SetNWBool("PigText", true);
	ply:SetNWBool("UCText", true);
	ply.ShouldRank = 1;
	ply:SetNWInt("PigRank", 1);
	ply:SetNWBool("Taunting", false);
	ply.ShouldStomp = false;
	ply.Birdee = nil;
	ply.CanBite = true;

	if (self:GetState() == self.STATE_WAITING) then //player just joined and spawned, check if enough players
		self:StartCheck();
	end
	if ((self:GetState() == self.STATE_PLAYING || self:GetState() == self.STATE_END) && team.NumPlayers(self.TEAM_DEADPIGS) > 0) then //new players while the game is playing get sent to dead pigs
		timer.Simple(.1, function()
			ply:SetTeam(self.TEAM_DEADPIGS);
			ply:KillSilent();
		end);
	end
	
end


function GM:UpdatePlayerHull(ply)
	umsg.Start("UCHull", ply)
	umsg.End()
end


function GM:DeleteChimeraRagdoll(ply)
	umsg.Start("UCRagSkin")
		umsg.Entity(ply);
	umsg.End()
end


local function ConvertRank(ply)
	local rank = ply:GetNWInt("PigRank", 1);
	local rtn = "Ensign";
	if (rank == 2) then
		rtn = "Captain";
	end
	if (rank == 3) then
		rtn = "Major";
	end
	if (rank == 4) then
		rtn = "Colonel";
	end
	return rtn;
end


function GM:PlayerSpawn(ply)

	if (ValidEntity(ply.Birdee)) then
		ply.Birdee:Remove();
	end
	
	self:PlayerSetModel(ply);
	ply:UnSpectate();
	ply.Speccing = false;
	ply:SetNWEntity("UCSpec", NULL);
	ply:SetTeamAuto();
	ply:SetNWBool("Taunting", false);
	ply:SetRenderMode(RENDERMODE_NORMAL);
	ply:UnLock();
	
	if (timer.IsTimer(tostring(ply) .. "TauntTimer")) then
		timer.Destroy(tostring(ply) .. "TauntTimer");
	end
	
	local spawntype = 1;
	local map = game.GetMap();
	if (file.Exists("chimera_spawns/" .. map .. ".txt")) then //read from file
		spawntype = 2;
	elseif (#ents.FindByClass("chimera_spawn") >= 1) then //spawn at random chimera spawn
		spawntype = 3;
	else //map not supported
		ply:ChatPrint(map .. " isn't supported by this gamemode!");
		spawntype = 1;
	end
	
	if (!ValidEntity(ply.PigMask)) then
		local pig = ents.Create("pigmask_scared");
		pig:SetPos(ply:GetPos());
		pig:SetAngles(ply:GetAngles());
		pig:SetPlayer(ply);
		pig:Spawn();
		pig:SetParent(ply);
		ply.PigMask = pig;
	end
	
	ply:SetScared(false);
	ply.PigMask:DrawShadow(false);
	if (timer.IsTimer(tostring(ply) .. "UCScared")) then
		timer.Destroy(tostring(ply) .. "UCScared");
	end
	
	self:UpdatePlayerHull(ply);
	
	timer.Simple(.1, function()
		if (ValidEntity(ply)) then
			self:UpdatePlayerHull(ply);
		end
	end)
	
	if (ply:IsChimera()) then
		if (self:GetState() == self.STATE_WAITING) then
			ply:RemoveChimera();
			ply:SetNWBool("IsChimera", false);
			ply:Spawn();
		else
			ply:SetHull(self.UCHullSizeMin, self.UCHullSizeMax);
			ply:SetHullDuck(self.UCHullSizeMin, self.UCHullSizeMax);
			ply:SetViewOffset(Vector(0, 0, self.UCViewOffset));
			ply:SetViewOffsetDucked(Vector(0, 0, self.UCViewOffset));
			ply:SetJumpPower(250);
		end
	else
		ply:SetColor(255, 255, 255, 255);
		ply:DrawShadow(true);
		ply.PigMask:DrawShadow(false);
		local rank = ply:GetNWInt("PigRank", 1);
		if (rank != ply.ShouldRank) then //they've changed ranks
			ply:SetNWInt("PigRank", ply.ShouldRank);
			if (ply.ShouldRank < rank) then //demoted
				local str = "You've been demoted!  You're now a ";
				if (ply.ShouldRank == 1) then
					str = (str .. "lowly ");
				end
				ply:ChatPrint(str .. ConvertRank(ply) .. "!");
			else //promoted
				ply:ChatPrint("You've been promoted to the rank of " .. ConvertRank(ply) .. "!");
			end
		end
		
		ply:SetSkin((ply:GetNWInt("PigRank", 1) - 1));
		ply:SetBodygroup(1, (ply:GetNWInt("PigRank", 1) - 1));
		ply.PigMask:SetSkin(ply:GetSkin());
		ply.PigMask:SetBodygroup(1, ply:GetBodygroup(1));
		
		if (ply:GetNWInt("PigRank", 1) == 4) then
			ply:SetBodygroup(2, 1);
			ply.PigMask:SetBodygroup(2, 1);
			ply:SetBodygroup(1, 0);
			ply.PigMask:SetBodygroup(1, 0);
		else
			ply:SetBodygroup(2, 0);
			ply.PigMask:SetBodygroup(2, 0);
		end
	
		ply:StripWeapons();
		ply:SetSpeed(self.plySpeed);
		ply:SetCrouchedWalkSpeed(self.plyCrouchSpeed);
		ply:SetHull(self.plyHullSizeMin, self.plyHullSizeMax);
		ply:SetHullDuck(self.plyHullSizeMin, self.plyHullSizeMaxD);
		ply:SetViewOffset(Vector(0, 0, self.plyViewOffset));
		ply:SetViewOffsetDucked(Vector(0, 0, self.plyViewOffsetD));
		ply:SetJumpPower(200);
	end
	if (self:GetState() == self.STATE_PLAYING) then
		ply:UnLock();
		ply.UCSprinting = false;
		ply:SetSprint(1);
		ply:DestroySprintTimer();
		ply.Biting = false
		ply.Roaring = false;
		umsg.Start("UCStopSprint", ply)
		umsg.End()
		local str = "surface.PlaySound(\"chimerahunt/spawn.wav\")";
		if (ply:IsChimera()) then
			if (ply:GetNWBool("UCText")) then
				timer.Simple(self.UCTextTime, function()
					if (ValidEntity(ply)) then
						ply:SetNWBool("UCText", false);
					end
				end);
			end
			str = (str .. " timer.Simple(.5, surface.PlaySound, \"chimerahunt/chimera/roar.wav\")");
			ply:SetWalkSpeed(self.UCSpeed);
			ply:SetCrouchedWalkSpeed(self.UCSpeed);
			ply:Give("chimera_weapon");
			if (ply:FlashlightIsOn()) then
				ply:Flashlight(false);
			end
			
			if (spawntype != 1) then
				
				if (spawntype == 2) then
					
					local map = game.GetMap();
					local file = file.Read("chimera_spawns/" .. map .. ".txt");
					file = string.Explode("\n", file);
					local pos, ang = string.Explode(" ", file[1]), string.Explode(" ", file[2]);
					local x, y, z = tonumber(pos[1]), tonumber(pos[2]), tonumber(pos[3]);
					local p, yy, r = tonumber(ang[1]), tonumber(ang[2]), tonumber(ang[3]);
					
					ply:SetPos(Vector(x, y, z));
					ply:SetAngles(Angle(p, yy, r));
					
				else
					
					local spawns = ents.FindByClass("chimera_spawn");
					ply:SetPos(spawns[math.random(1, #spawns)]:GetPos());
					ply:SetAngles(spawns[math.random(1, #spawns)]:GetAngles());
					
				end
				
			end
			
		else
			if (ply:GetNWBool("PigText")) then
				timer.Simple(self.UCTextTime, function()
					if (ValidEntity(ply)) then
						ply:SetNWBool("PigText", false);
					end
				end);
			end
		end
		ply:SendLua(str);
	end
	if (self:GetState() == self.STATE_COUNTDOWN) then
		timer.Simple(.1, function()
			if (self:GetState() == self.STATE_COUNTDOWN) then
				ply:Lock()
			end
		end);
	end
	
end


function GM:PlayerDeathThink(ply)
	if (self:GetState() != self.STATE_PLAYING && self:GetState() != self.STATE_END) then
		if (ply.NextSpawnTime && ply.NextSpawnTime > CurTime()) then
			return;
		end
		if (ply:KeyPressed(IN_ATTACK) || ply:KeyPressed(IN_ATTACK2) || ply:KeyPressed(IN_JUMP)) then
			ply:Spawn()
		end
	end
end


function GM:PlayerDeath(ply, inf, ply2)

	ply.NextSpawnTime = (CurTime() + 3);
	
	ply:SetScared(false);
	ply:SetNWInt("SpecMode", 1);
	ply:SetNWBool("Taunting", false);
	ply:UnLock();
	ply:SetRenderMode(RENDERMODE_NORMAL);
	
	if (timer.IsTimer(tostring(ply) .. "TauntTimer")) then
		timer.Destroy(tostring(ply) .. "TauntTimer");
	end
	
	if (!ply:IsChimera()) then
		ply:SetTeam(self.TEAM_DEADPIGS);
		umsg.Start("UCRagSkin")
			umsg.Entity(ply);
		umsg.End()
	else
	
		local bird = ents.Create("prop_physics");
		bird:SetModel("models/nintendo/mother/birdgib.mdl");
		
		/*local atch = self:GetChimera().Chimera:LookupAttachment("bird");
		atch = self:GetChimera().Chimera:GetAttachment(atch);
		bird:SetPos(atch.Pos);*/
		bird:SetPos(self:GetChimera():GetPos());
		
		bird:SetAngles(ply:GetAngles());
		bird:Spawn();
		bird:SetCollisionGroup(COLLISION_GROUP_DEBRIS);
		
		self:GetChimera().Birdee = bird;
		
		ply:CreateRagdoll();
		if (ValidEntity(ply.PigMask)) then
			ply.PigMask:DrawShadow(false);
		end
		if (ValidEntity(ply.Chimera)) then
			ply.Chimera:Remove();
		end
	end
	
	if (self:GetState() == self.STATE_PLAYING) then
		if (ply:IsChimera()) then
			self:SetState(self.STATE_END);
			self:StopRoundTimer();
			self:StartEndTimer();
			self:PigsWin();
			self:DeleteChimeraRagdoll(ply);
		else
			ply.ShouldRank = 1;
			if (ply:LastAlive()) then
				self:SetState(self.STATE_END);
				self:StopRoundTimer();
				self:StartEndTimer();
				self:ChimeraWin();
				self:GetChimera():GetActiveWeapon():SetNextSecondaryFire(CurTime());
				self:GetChimera():SendLua("LocalPlayer():GetActiveWeapon().SecondDelay = 0;");
			else
				local num = (#player.GetAll() * .5);
				num = math.floor(num);
				if (!GetGlobalBool("RoundTimer") && #player.GetAll() >= self.MinForRoundTimer && #self:AlivePlayers() <= math.Clamp(num, 1, 4)) then
					self:StartRoundTimer();
				end
			end
			
		end
	end
	
end


function GM:StartRoundTimer()
	self:StopRoundTimer();
	for k, v in ipairs(player.GetAll()) do
		//v:SendLua("surface.PlaySound(\"chimerahunt/round_timer.wav\")");
		v:SendLua("surface.PlaySound(\"chimerahunt/tie.wav\")");
	end
	umsg.Start("RoundTimerUpdate")
		umsg.Long((CurTime() + (60 * GAMEMODE.RoundTimer)));
	umsg.End()
	SetGlobalBool("RoundTimer", true);
	self.RoundTime = (CurTime() + (60 * self.RoundTimer));
	timer.Simple(.25, function() self.CanAdd = true end);
end

function GM:StopRoundTimer()
	SetGlobalBool("RoundTimer", false);
end


function GM:Tie()
	for k, v in ipairs(player.GetAll()) do
		//v:SendLua("surface.PlaySound(\"chimerahunt/tie.wav\")");
		v:SendLua("surface.PlaySound(\"chimerahunt/round_timer.wav\")");
	end
end

function GM:ChimeraWin()
	for k, v in ipairs(player.GetAll()) do
		if (v:IsChimera()) then
			v:SendLua("surface.PlaySound(\"chimerahunt/chimera_win.wav\")");
		else
			v:SendLua("surface.PlaySound(\"chimerahunt/pigs_lose.wav\")");
		end
	end
end

function GM:PigsWin()
	for k, v in ipairs(player.GetAll()) do
		if (v:IsChimera()) then
			v:SendLua("surface.PlaySound(\"chimerahunt/chimera_lose.wav\")");
		else
			v:SendLua("surface.PlaySound(\"chimerahunt/pigs_win.wav\")");
		end
	end
end


function GM:FreezePlayers()
	for k, v in ipairs(player.GetAll()) do
		v:Lock();
	end
end


function GM:PlayerDisconnected(ply)
	if (self:GetState() == self.STATE_PLAYING) then
		if (ply:IsChimera()) then
			self:SetState(self.STATE_END);
			self:StartEndTimer();
			self:PigsWin();
		else
			if (ply:Alive()) then
				local num = (#player.GetAll() * .8);
				num = math.ceil(num);
				if (!GetGlobalBool("RoundTimer") && #player.GetAll() >= self.MaxForRoundTimer && #self:AlivePlayers() <= math.Clamp(num, 1, 5)) then
					self:StartRoundTimer();
				end
				local tbl = {};
				for k, v in ipairs(player.GetAll()) do
					if (v:Alive() && !v:IsChimera()) then
						table.insert(tbl, v);
					end
				end
				if (#tbl == 1) then
					self:SetState(self.STATE_END);
					self:StartEndTimer();
					self:ChimeraWin();
					self:GetChimera():GetActiveWeapon():SetNextSecondaryFire(CurTime());
					self:GetChimera():SendLua("LocalPlayer():GetActiveWeapon().SecondDelay = 0;");
				end
			end
		end
	end
	
	if (ValidEntity(ply.Birdee)) then
		ply.Birdee:Remove();
	end
	
end


function GM:SendKillNotice(ply, ply2, icon)
	umsg.Start("SendKillNotice")
		umsg.Entity(ply);
		umsg.Entity(ply2);
		umsg.String(icon);
	umsg.End()
end


function GM:SetNewChimera()

	local ply = player.GetAll();
	
	if (#ply <= 1) then
		return;
	end
	
	local tbl = {};
	local uc = self:GetChimera();
	
	if (ValidEntity(uc)) then //there was a previous game
		for k, v in ipairs(ply) do
			if (v != uc) then
				table.insert(tbl, v);
			end
		end
		uc:RemoveChimera();
		uc:SetNWBool("IsChimera", false);
	else
		tbl = ply;
	end
	
	local rnd = math.random(1, #tbl);
	ply = tbl[rnd];

	ply:SetNWBool("IsChimera", true);
	ply:SetupChimera();

end


function GM:AlivePlayers()
	local tbl = {};
	for k, v in ipairs(player.GetAll()) do
		if (v:Alive() && !v:IsChimera()) then
			table.insert(tbl, v);
		end
	end
	return tbl;
end


function GM:StartCheck()
	if (self:GetState() == self.STATE_WAITING) then
		
		local ply = player.GetAll();
		if (#ply >= self.NumPlayers) then //are there now enough players to start?
			self:FreezePlayers();
			self:SetState(self.STATE_STARTING);
			timer.Create("UCGameStarting", self.StartDelay, 1, self.ResetGame, self);
		end
		
	end
end


function GM:StartEndTimer()
	SetGlobalBool("RoundTimer", false);
	timer.Create("UCEndTimer", self.RoundEndTime, 1, function()
		self:ResetGame();
	end);
end


function GM:EndGame()
	
	self:SetState(self.STATE_END);
	
	timer.Simple(self.RoundEndTime, self.ResetGame, self);
	
end


function GM:ResetGame()
	
	if (timer.IsTimer("UCGameStarting")) then
		timer.Destroy("UCGameStarting");
	end
	if (timer.IsTimer("UCEndTimer")) then
		timer.Destroy("UCEndTimer");
	end
	game.CleanUpMap();
	RemoveDoors();
	if (#player.GetAll() < self.NumPlayers) then
		self:SetState(self.STATE_WAITING);
	else
		self:SetState(self.STATE_PLAYING);
		self:SetNewChimera();
	end
	for k, v in ipairs(player.GetAll()) do
		v:KillSilent();
		v:Spawn();
	end
	
	self.CanAdd = false;
	SetGlobalBool("RoundTimer", false);
	
end


function GM:EntityTakeDamage(ent, inflictor, attacker, amount, dmg)
	if (dmg:GetDamage() < 100) then
		if (ent:IsPlayer() && ent:IsChimera() && attacker:GetClass() != "trigger_hurt") then
			dmg:ScaleDamage(0);
		end
	end
end


function GM:CanPlayerSuicide(ply)
	return !ply:IsChimera();
end


function GM:PlayerSwitchFlashlight(ply, on)
	if (ply:IsChimera()) then
		return false;
	end
end


function GM:Think()
	
	for k, v in ipairs(player.GetAll()) do
		
		if (!v:IsScared()) then
			if (v:GetSprint() <= 0) then
			
				v.UCSprinting = false;
				v.SprintRegen = false;
				
				if (v:IsChimera()) then
					v:SetRunSpeed(self.UCSpeed);
				else
					v:SetSpeed(self.plySpeed);
					if (!v:IsSprintTimer()) then
						v:MakeSprintTimer();
					end
				end
				
			else
				if (!v:IsChimera()) then
					if (v.UCSprinting) then
						v:SetSpeed(self.plySprintSpeed);
						local num = v:GetSprint();
						local degen = (self.plySprintDegenRate - ((v:GetNWInt("PigRank", 1) * .75) * .0008));
						num = math.Approach(num, 0, degen);
						v:SetSprint(num);
					else
						if (v.SprintRegen && !v:IsScared()) then
							local num = v:GetSprint();
							num = math.Approach(num, 1, self.plySprintRegenRate);
							v:SetSprint(num);
						end
					end
				end
			end
		else
			
			//be scared
			v:SetSpeed(self.plyScaredSpeed);
			
		end
		
		if (v:IsChimera()) then
			local num = v:GetSprint();
			if (v:KeyDown(IN_SPEED) && !v:Crouching() && !v:KeyDown(IN_BACK) && (v:KeyDown(IN_FORWARD) || v:KeyDown(IN_MOVELEFT) || v:KeyDown(IN_MOVERIGHT))) then
				if (v:GetSprint() > 0) then
					v:SetRunSpeed(self.UCSprintSpeed);
					num = math.Approach(num, 0, self.UCSprintDegenRate);
				else
					v:SetRunSpeed(self.UCSpeed);
				end
			else
				v:SetRunSpeed(self.UCSpeed);
				num = math.Approach(num, 1, self.UCSprintRegenRate);
			end
			v:SetSprint(num);
		else
			v:BeAPiggy();
		end
		
		local ent = v:GetNWEntity("UCSpec");
		if (v:GetNWInt("SpecMode") == 1 && ValidEntity(ent) && ent != v) then
			v:SetPos(ent:GetPos());
		end
		
		//Round Timer
		if (GetGlobalBool("RoundTimer")) then
		
			if (CurTime() >= self.RoundTime) then
				self:SetState(self.STATE_END);
				self:StartEndTimer();
				self:Tie();
				for k, v in ipairs(player.GetAll()) do
					if (v:Alive()) then
						if (!v:IsChimera()) then
							v.ShouldRank = math.Clamp((v.ShouldRank - 1), 1, 4);
						end
						v:Kill();
					end
				end
				self:DeleteChimeraRagdoll(ply);
			end
			
		end
		
	end

end
